/**
 * This plugin implements a decorative frame framework based on the 
 * <a href="http://www.webdesignerwall.com/tutorials/css-decorative-gallery/">CSS Decorative Gallery</a> 
 * by <a href="http://www.webdesignerwall.com">Web Designer Wall</a>.<p>
 * 
 * For this plugin to work a picture has to be structured somehow as 
 * following:
 * <pre>
 *   &lt;a&gt;
 *     &lt;img src='...' /&gt;
 *     &lt;em&gt;title&lt;/em&gt;
 *   &lt;/a&gt;
 * </pre>
 * 
 * Where <code>&lt;a&gt;</code> can be almost any container element, for 
 * instance a <code>&lt;li&gt;</code> or <code>&lt;div&gt;</code> will 
 * also do, but it has always to be (relatively) positioned, for the new 
 * child element (for the frame) to be absolutely positioned.<p>
 * 
 * The generated code is almost the same, and as semantically correct 
 * and clean as originally, but it is more flexible, since you can use 
 * the same frame images for different image sizes.<p>
 * 
 * I'am using the new CSS3 <code>border-image</code> property, where 
 * available, Gecko & Webkit for now, the <code>jquery.borderImage.js</code> 
 * plugin for the rest of the world.<p>
 * 
 * The generated code looks like:
 * <pre>
 *   &lt;a class='df FRAME_NAME'&gt;
 *     &lt;div class='frame' &gt;&lt;/div&gt;
 *     &lt;img src='...' /&gt;
 *     &lt;em&gt;title&lt;/em&gt;
 *   &lt;/a&gt;
 * </pre>
 * Where the new element is absolutely positioned.<p>
 * 
 * Be also aware that from the provided frames, some are only usable 
 * on white background, specially:
 * <ul>
 *   <li><code>mask</code></li>
 * </ul>
 * 
 * For styling, you will need following CSS files:
 * <dl>
 *   <dt><code>css/gallery.css</code></dt>
 *   <dd>Sample decorative gallery styling</dd>
 *   <dt><code>css/decorative-frames.css</code></dt>
 *   <dd>Sample picture frames styling</dd>
 * </dl>
 * 
 * @author Michael Moossen mmoossen@gmail.com
 * @see http://decorative-frames.googlecode.com/
 */
(function($) {

	// first, ensure all browsers know how to search in an array
	if (!Array.indexOf) {
		Array.prototype.indexOf = function(value) {
			for(var i = 0; i < this.length; i++) {
				if (this[i] === value) {
					return i;
				}
			}
			return -1;
		};
	}

	/** Available frames, cached by name. */
	var /** {AssocArray<String, Frame>} */ frames = {};
	
	/** List of available frame names. */
	var /** {Array<String>} */ frameNames = [];

	/**
	 * Plugin entry point.<p>
	 * 
	 * @param {String} frameName the name of the frame to apply
	 * @param {AssocArray} options optional options extending the settings
	 * 
	 * @return {JQuery} <code>this</code> for chaining
	 */
	jQuery.fn.decorativeFrame = function (frameName, options) {
		
		// extend the settings
		var opts = $.extend({}, jQuery.decorativeFrames.settings, options || {});
		// iterate the elements to apply the given frame
		return this.each(function() {
			var $cnt = $(this);
			// uninstall
			if (frameName == 'destroy') {
				// find the matching frame
				for (var i = 0; i < frameNames.length; i++) {
					var name = frameNames[i];
					if ($cnt.hasClass(name)) {
						// destroy it
						frames[name].destroyFrame($cnt, opts);
						return true;
					}
				}
				// not found, nothing to do
				return true;
			}
			// get the frame to install
			var frame = frames[frameName];
			if (frame) {
				// install it
				frame.createFrame($cnt, opts);
			}
		});
	};
	
	/** 
	 * The frame object, providing all the functionallity.<p>
	 * 
	 * @param {String} pName the frame name
	 * @param {String} pImgUrl the frame image URL
	 * @param {Frame} pBackground if needed a second frame as background
	 */
	var Frame = function(/** {String} */ pName, /** {String} */ pImgUrl, /** {Frame} */ pBackground) {
		
		/** The frame name. */
		/** {String} */ this.name = pName;
		
		/** The frame image. */
		/** {String} */ this.imgUrl = pImgUrl;
		
		/** The background frame. */
		/** {String} */ this.background = pBackground;
		
		/**
		 * Uninstalls this frame from the given container.<p>
		 * 
		 * @param {jQuery} $cnt the container element
		 * @param {AssocArray} opts the options to use
		 */
		this.destroyFrame = /** {void} */ function(/** {jQuery} */ $cnt, /** {AssocArray} */ opts) {
			
			// remove marker classes
			$cnt.removeClass(opts.cntClass).removeClass(this.name);
			// remove frame
			$cnt.find('div.' + opts.frameClass).remove();
			// remove background 
			$cnt.find('div.' + opts.bgClass).remove();
		};
		
		/**
		 * Installs this frame to the given container.<p>
		 * 
		 * @param {jQuery} $cnt the container element
		 * @param {AssocArray} opts the options to use
		 */
		this.createFrame = /** {void} */ function(/** {jQuery} */ $cnt, /** {AssocArray} */ opts) {
			
			// mark frame container
			$cnt.addClass(opts.cntClass).addClass(this.name);
			// mark original image
			var $img = $cnt.find('img');
			// get the image dimension
			var w = $img.outerWidth(), h = $img.outerHeight();
			// create frame
			var $frame = $('<div>')
			  .addClass(opts.frameClass)
			  .css({
					width: w + 'px',
					height: h + 'px'
				});
			if (this.name == 'polaroid' || this.name == 'watercolor') {
				$img.before($frame);
			} else {
				$img.after($frame);
			}
			if (!$.browser.support.borderImage) {
				var borderImage = 'url("' + this.imgUrl + '")';
				borderImage += ' ' + parseInt($frame.css('border-top-width'));
				borderImage += ' ' + parseInt($frame.css('border-right-width'));
				borderImage += ' ' + parseInt($frame.css('border-bottom-width'));
				borderImage += ' ' + parseInt($frame.css('border-left-width'));
				$frame.borderImage(borderImage);
			}
			
			// if there a background
			if (this.background) {
				// create background
				var $bg = $('<div>')
				  .addClass(opts.bgClass)
				  .css({
						width: w + 'px',
						height: h + 'px'
					});
				$img.before($bg);
				if (!$.browser.support.borderImage) {
					var bgBorderImage = 'url("' + this.background.imgUrl + '")';
					bgBorderImage += ' ' + parseInt($bg.css('border-top-width'));
					bgBorderImage += ' ' + parseInt($bg.css('border-right-width'));
					bgBorderImage += ' ' + parseInt($bg.css('border-bottom-width'));
					bgBorderImage += ' ' + parseInt($bg.css('border-left-width'));
					$bg.borderImage(bgBorderImage);
				}
			}
		};
	};
	
	// expose the API	
	$.decorativeFrames = {
		
		/**
		 * The default settings.<p>
		 */
		settings: {
			cntClass: 'df',
			frameClass: 'frame',
			bgClass: 'bg'
		},

		/**
		 * Adds the given frame to the repository.<p>
		 * 
		 * @param {Frame} frame the frame to add
		 */
		add: /** {void} */ function(/** {Frame} */ frame) {
			
			if (frameNames.indexOf(frame.name) > -1) {
				throw 'frame "' + frame.name + '" already registered';
			}
			frameNames.push(frame.name);
			frames[frame.name] = frame;
		},
		/** List of available frame names. */
		all: /** {Array<String>} */ frameNames
	};
	
	// add some sample frames
	$.decorativeFrames.add(new Frame('polaroid', 'frames/polaroid.png'));
	$.decorativeFrames.add(new Frame('black-frame', 'frames/black-frame.png'));
	$.decorativeFrames.add(new Frame('grung', 'frames/grung.png'));
	$.decorativeFrames.add(new Frame('brush', 'frames/brush.png'));
	$.decorativeFrames.add(new Frame('mask', 'frames/mask.png'));
	$.decorativeFrames.add(new Frame('cut-corner', 'frames/cut-corner.png'));
	$.decorativeFrames.add(new Frame('watercolor', 'frames/watercolor.png'));
	$.decorativeFrames.add(new Frame('gold-frame', 'frames/gold-frame.png'));
	// these two need a background frame
	$.decorativeFrames.add(new Frame('stamp', 'frames/stamp.png', new Frame('stamp-bg', 'frames/stamp-bg.png')));
	$.decorativeFrames.add(new Frame('round-corner', 'frames/round-corner.png', new Frame('round-bg', 'frames/round-bg.png')));
})(jQuery);
